#ifndef PLATEBASEA_HPP
#define PLATEBASEA_HPP

//#define USEREG


//#include "platebase.hpp"

#include <opencv2/opencv.hpp>

#include "opencv2/reg/mapaffine.hpp"
#include "opencv2/reg/mapshift.hpp"
#include "opencv2/reg/mapprojec.hpp"
#include "opencv2/reg/mappergradshift.hpp"
#include "opencv2/reg/mappergradeuclid.hpp"
#include "opencv2/reg/mappergradsimilar.hpp"
#include "opencv2/reg/mappergradaffine.hpp"
#include "opencv2/reg/mappergradproj.hpp"
#include "opencv2/reg/mapperpyramid.hpp"

class PlateBaseA //: public PlateBase
{
public:
    cv::Mat image;
    PlateBaseA()
      //  : PlateBase()
    {
        cv::Mat img1 = cv::imread("/home/u/build-untitled-Desktop-Release/67r.png", CV_LOAD_IMAGE_UNCHANGED);

        // Convert to double, 3 channels
        img1.convertTo(image, CV_64F);
    }





    static void showDifference(const cv::Mat& image1, const cv::Mat& image2, const char* title)
    {
        cv::Mat img1, img2;
        image1.convertTo(img1, CV_32FC3);
        image2.convertTo(img2, CV_32FC3);
        if(img1.channels() != 1)
            cv::cvtColor(img1, img1, CV_RGB2GRAY);
        if(img2.channels() != 1)
            cv::cvtColor(img2, img2, CV_RGB2GRAY);



        cv::Mat imgDiff;
        img1.copyTo(imgDiff);
        imgDiff -= img2;
        imgDiff /= 2.f;
        imgDiff += 128.f;

        cv::Mat imgSh;
        imgDiff.convertTo(imgSh, CV_8UC3);
        cv::imshow(title, imgSh);
    }


    static cv::Matx33d MapP(const cv::Mat &img1, const cv::Mat &img2)
    {
        // Register
        cv::Ptr<cv::reg::MapperGradProj> mapper = cv::makePtr<cv::reg::MapperGradProj>();
        cv::reg::MapperPyramid mappPyr(mapper);
        cv::Ptr<cv::reg::Map> mapPtr = mappPyr.calculate(img1, img2);

        // Print result
        cv::reg::MapProjec* mapProj = dynamic_cast<cv::reg::MapProjec*>(mapPtr.get());
        mapProj->normalize();

        cv::Matx33d res;
        cv::Mat(mapProj->getProjTr()).copyTo(res);
#if 1
        // Display registration accuracy
        //         cv::Mat dest;
        //         mapProj->inverseWarp(img2, dest);

        cv::Mat dest2;
        cv::warpPerspective(img2, dest2, res.inv(), img2.size());

        //        showDifference(dest2, img2, "dest2");

        showDifference(img1, dest2, "Image difference: pixel registered");


        cv::Mat img2warp;
        dest2.convertTo(img2warp,CV_8U);
        cv::imshow("img2warp",img2warp);

        std::cout<<"res="<<res<<"\n"<<std::flush;
#endif


        return res;
    }


    static void testProjective(const cv::Mat& img1)
    {
        cv::Mat img2;

        // Warp original image
        cv::Matx<double, 3, 3> projTr(1., 0., 0., 0., 1., 0., 0.0001, 0.0001, 1);
        cv::reg::MapProjec mapTest(projTr);
        mapTest.warp(img1, img2);
        showDifference(img1, img2, "Image difference");

        cv::Matx33d res;

        res=MapP(img1,img2);

        cvDestroyWindow("Image difference");

    }


    static double ScaleToD(const cv::Mat &m8, cv::Mat &m64, double maxL=400)
    {
        double scale=1;
        double scalex=maxL/m8.cols;
        double scaley=maxL/m8.rows;

        if(scale>scalex) scale=scalex;
        if(scale>scaley) scale=scaley;

        // Convert to double, 3 channels
        m8.convertTo(m64, CV_64F);
        if(scale!=1)
            cv::resize(m64,m64,cv::Size(),scale,scale);
        return scale;
    }


    static void ShowProj(const cv::Mat &frame, cv::Matx33d trans, cv::Size markSize)
    {
        std::vector<cv::Point> vtx(4);
        vtx[0]=cv::Point(0,0);
        vtx[1]=cv::Point(0,markSize.height-1);
        vtx[2]=cv::Point(markSize.width-1,markSize.height-1);
        vtx[3]=cv::Point(markSize.width-1,0);

        for(auto &v1:vtx)
        {
            cv::Vec3d vtxn=trans*cv::Vec3d(v1.x, v1.y, 1);
            v1=cv::Point(vtxn[0]/vtxn[2], vtxn[1]/vtxn[2]);
        }

        cv::polylines(frame, vtx, true, cv::Scalar(0,0,255), 3);

        //         cv::imshow("ShowProj", frame);
        //        cv::waitKey(-1);

    }


    void ShowProj(const cv::Mat &frame, cv::Matx33d trans)
    {
        ShowProj(frame, trans, image.size());
    }

    static cv::Matx33d GetProj(const cv::Mat &mark, const cv::Mat &frame, cv::Rect roi)
    {
        cv::Mat frameroi;
        frame(roi).convertTo(frameroi, mark.type());
        cv::resize(frameroi,frameroi,mark.size());

        cv::imwrite("roi.png",frame(roi));


        cv::Matx33d res=MapP(mark,frameroi);

        cv::Matx33d tpre(((double)roi.width)/mark.cols, 0, roi.x,
                         0, ((double)roi.height)/mark.rows, roi.y,
                         0, 0, 1);

        cv::Matx33d trans=tpre*res;

        return trans;
    }


    static cv::Matx33d GetProj2(const cv::Mat &mark, const cv::Mat &frame, cv::Matx33d transinit)
    {
        cv::Mat frameroi;

        cv::warpPerspective(frame, frameroi, transinit.inv(), mark.size());

        cv::Mat frameroif;

        frameroi.convertTo(frameroif, mark.type());

        cv::Matx33d res=MapP(mark,frameroif);

        cv::Matx33d trans=transinit*res;

        return trans;
    }

    template<typename T>
    static void Expand1D(T &start, T &length, double scale)
    {
        start-=length*(scale-1)/2;
        length*=scale;
    }


    cv::Matx33d GetPlateProjection(const cv::Mat &m, cv::Rect digitBoundingBox[2])
    {
        cv::Rect roi=digitBoundingBox[0]|digitBoundingBox[1];

        Expand1D(roi.x, roi.width, 1.2);

        Expand1D(roi.y, roi.height, 1.2);

        std::cout<<"roi="<<roi<<"\n"<<std::flush;

        roi=roi&cv::Rect(0,0,m.cols,m.rows);

        cv::Matx33d res=GetProj(image, m, roi);

        //        ShowProj(m, res, img2.size());
        return res;
    }


    static void ttest(std::string markPath, std::string scenePath)
    {
        cv::Mat ma=cv::imread(markPath,0);
        cv::Mat m=cv::imread(scenePath, 0);

        cv::Canny(ma,ma,100,400);

        cv::Canny(m,m,100,400);

        //         cv::threshold(ma, ma,128,255,cv::THRESH_BINARY);

        //         cv::threshold(m, m, 180, 255, cv::THRESH_BINARY);

        cv::Mat ma64;
        ma.convertTo(ma64,CV_64F);

        cv::Mat m64;
        m.convertTo(m64,CV_64F);

        cv::Matx33d res=GetProj(ma64,m64, cv::Rect(0,0,m64.cols,m64.rows));

        cv::waitKey();
    }


};

#endif // PLATEBASEA_HPP

